package com.metamech.wocky;

import java.io.IOException;
import java.io.Writer;
import java.net.Socket;
import java.util.Hashtable;
import java.util.Iterator;

import com.metamech.jabber.Authenticator;
import com.metamech.jabber.JabberID;
import com.metamech.jabber.Session;
import com.metamech.jabber.StatusListener;
import com.metamech.jabber.xml.Packet;
import com.metamech.jabber.xml.PacketListener;
import com.metamech.jabber.xml.PacketQueue;
import com.metamech.jabber.xml.ProcessThread;
import com.metamech.log.Log;

/**
 * Title:
 * Description:
 * Copyright:    Copyright (c) 2001
 * Company:
 * @author
 * @version 1.0
 */

public class JabberModel {
	String version = "v. 1.0 - ch. 4";
	String serverName;
	String serverAddress;
	String serverPort;
	String user;
	String password;
	String auth;
	String resource;
	Session session = new Session();
	PacketQueue packetQueue;
	PacketListener authHandler = new AuthHandler(this);
	Hashtable resultHandlers = new Hashtable();
	Authenticator authenticator = new Authenticator();
	
	JabberModel(TestThread qThread) {
		packetQueue = qThread.getQueue();
		qThread.addListener(new OpenStreamHandler(),"stream:stream");
		qThread.addListener(new CloseStreamHandler(),"/stream:stream");
		qThread.addListener(new MessageHandler(),"message");
		qThread.addListener(authHandler,"jabber:iq:auth");
	    qThread.addListener(new IQHandler(this),"iq");
	    qThread.addListener(new RosterHandler(),"jabber:iq:roster");
	}

	// Create the global queue and session everyone works with
	
	public int getSessionStatus() {
		return session.getStatus();
	}
	
	public String getVersion(){ 
		return version; 
	}
	
	public String getServerName(){
		return serverName;
	}
	
	public void setServerName(String serverName){
		this.serverName = serverName;
	}
	
  	public String getServerAddress(){
  		return serverAddress;
  	}
  	
  	public void setServerAddress(String serverAddress) {
  		this.serverAddress = serverAddress;
  	}
  	
  	public String getPort(){
  		return serverPort;
  	}
  	
  	public void   setPort(String serverPort){
  		this.serverPort = serverPort;
  	}

  	
  	public String getUser(){
  		return user;
  	}
  	
  	public void setUser(String usr){
  		user = usr; 
  	}
  	
  	public String getPassword() {
  		return password;
  	}
  	
    public void setPassword(String pass) {
    	password = pass;
    }
    
  	public String getAuthMode() {
  		return auth;
  	}
  	
    public void setAuthMode(String mode) { 
    	auth = mode; 
    }
    
  	public String getResource(){
  		return resource;
  	}
  	
  	public void   setResource(String res){
  		resource = res;
  	}
  	
  	public void addResultHandler(String id_code,PacketListener listener){
  	    resultHandlers.put(id_code,listener);
  	}
  	
  	public PacketListener removeResultHandler(String id_code){
  	    return (PacketListener)resultHandlers.remove(id_code);
  	}
  	  
  	public void addStatusListener(StatusListener listener){
  		// Create the sesssion and get it setup
  		session.addStatusListener(listener);
  	}

  	public void removeStatusListener(StatusListener listener){
  		session.removeStatusListener(listener);
  	}

  	public void connect() throws IOException {

	    // Create a socket
	    session.setSocket(new Socket(serverAddress,Integer.parseInt(serverPort)));
	    session.setStatus(Session.CONNECTED);

	    // Process incoming messages
	    (new ProcessThread(packetQueue,session)).start();
	
	    // Send our own "open stream" packet with the server name
	    Writer out = session.getWriter();
	    session.setJID(new JabberID(user,serverName,resource));
	    Log.trace("menerima : "+session.getJID().toString());
	    out.write("<?xml version='1.0' encoding='UTF-8' ?><stream:stream to='");
	    out.write(serverName);
	    out.write("' from='");
	    out.write(user + "@" + serverName);
	    out.write("' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams'>");
	    out.flush();
	    
  	}

  	// Send close stream fragment
  	public void disconnect() throws IOException {
  		session.closeStream();
  	}

  	public void sendMessage(String recipient,
                          String subject,
                          String thread,
                          String type,
                          String id,
                          String body) throws IOException {
  		
	    Packet packet = new Packet("message");
	
	    if (recipient != null){
	    	packet.setTo(recipient);
	    }
	    if (id != null){
	    	packet.setID(id);
	    }
	    if (type != null){
	    	packet.setType(type);
	    }
	    if (subject != null){
	    	packet.getChildren().add(new Packet("subject",subject));
	    }
	    if (thread != null){
	    	packet.getChildren().add(new Packet("thread",thread));
	    }
	    if (body != null){
	    	packet.getChildren().add(new Packet("body",body));
	    }
	    Log.trace("send Message To :" + recipient);
	    packet.writeXML(session.getWriter());
  	}
  	
  	public void sendPresence(String recipient,
					          String type,
					          String show,
					          String status,
					          String priority) throws IOException {
		
  		Packet packet = new Packet("presence");
		
		if (recipient != null){
			packet.setTo(recipient);
		}
		if (type != null){
			packet.setType(type);
		}
		if (show != null){
			packet.getChildren().add(new Packet("show",show));
		}
		if (status != null){
			packet.getChildren().add(new Packet("status",status));
		}
		if (priority != null){
			packet.getChildren().add(new Packet("priority",priority));
		}
		packet.writeXML(session.getWriter());
	}
  	
  	public void register() throws IOException {
  		
  	    if (auth.equals("0k")){
  	      register0k();
  	    } else {
  	      registerPlain();
  	    }
  	}
  	void registerPlain() throws IOException {
  		Log.trace("Register Plain");
  	    Writer out = session.getWriter();
  	    out.write("<iq type='set' id='reg_id'><query xmlns='jabber:iq:register'><username>");
  	    out.write(this.user);
  	    out.write("</username><password>");
  	    out.write(this.password);
  	    out.write("</password></query></iq>");
  	    out.flush();
  	    addResultHandler("reg_id",new RegisterHandler(this));
  	  }

  	  void register0k() throws IOException {
  		Log.trace("Register OK");
  	    String token = authenticator.randomToken();
  	    String hash = authenticator.getZeroKHash(100,
  	                                             token.getBytes(),
  	                                             password.getBytes());
  	    Writer out = session.getWriter();
  	    out.write("<iq type='set' id='reg_id'><query xmlns='jabber:iq:register'><username>");
  	    out.write(this.user);
  	    out.write("</username><sequence>");
  	    out.write("100");
  	    out.write("</sequence><token>");
  	    out.write(token);
  	    out.write("</token><hash>");
  	    out.write(hash);
  	    out.write("</hash></query></iq>");
  	    out.flush();
  	    // Notice password is never sent or stored on server
  	    addResultHandler("reg_id",new RegisterHandler(this));
  	  }

  	  // Authenticate as user with given password
  	  int counter; // used to generate auth id's
  	  public void authenticate() throws IOException {
  	    if (auth.equals("0k")){
  	      authenticate0k();
  	    } else if (auth.equals("digest")){
  	      authenticateDigest();
  	    } else {
  	      authenticatePlain();
  	    }
  	  }

  	  // Authenticate as user with given password
  	  void authenticatePlain() throws IOException {
  		Log.trace("Authenticate Plain");
  	    addResultHandler("plain_auth_" + Integer.toString(counter),authHandler);
  	    Writer out = session.getWriter();
  	    out.write("<iq type=\'set\' id=\'plain_auth_");
  	    out.write(Integer.toString(counter++));
  	    out.write("\'><query xmlns=\'jabber:iq:auth\'><username>");
  	    out.write(this.user);
  	    out.write("</username><resource>");
  	    out.write(this.resource);
  	    out.write("</resource><password>");
  	    out.write(this.password);
  	    out.write("</password></query></iq>");
  	    out.flush();
  	    
  	    StringBuffer a = new StringBuffer();
  	    a.append("<iq type=\'set\' id=\"plain_auth_");
  	    a.append(Integer.toString(counter++));
  	    a.append("\"><query xmlns=\"jabber:iq:auth\"><username>");
  	    a.append(this.user);
  	    a.append("</username><resource>");
  	    a.append(this.resource);
  	    a.append("</resource><password>");
  	    a.append(this.password);
  	    a.append("</password></query></iq>");
  	    System.out.println(a.toString());
  	    
  	  }

  	  // Authenticate as user with given password
  	  void authenticateDigest() throws IOException {
  		Log.trace("Authenticate Digest");
  	    addResultHandler("digest_auth_" + Integer.toString(counter),authHandler);
  	    Writer out = session.getWriter();
  	    out.write("<iq type='set' id='digest_auth_");
  	    out.write(Integer.toString(counter++));
  	    out.write("'><query xmlns='jabber:iq:auth'><username>");
  	    out.write(this.user);
  	    out.write("</username><resource>");
  	    out.write(this.resource);
  	    out.write("</resource><digest>");
  	    out.write(authenticator.getDigest(session.getStreamID(),password));
  	    out.write("</digest></query></iq>");
  	    out.flush();
  	  }

  	  // Authenticate as user with given password
  	  void authenticate0k() throws IOException {
  		Log.trace("Authenticate OK");
  	    // Initiate authentication here... it is finished in AuthHandler
  	    Writer out = session.getWriter();
  	    out.write("<iq type='get' id='auth_get_");
  	    out.write(Integer.toString(counter++));
  	    out.write("'><query xmlns='jabber:iq:auth'><username>");
  	    out.write(this.user);
  	    out.write("</username></query></iq>");
  	    out.flush();
  	  }
  	public void sendRosterGet() throws IOException {
  	    Packet packet = new Packet("iq");
  	    packet.setType("get");
  	    packet.setID("roster_get");
  	    Packet query = new Packet("query");
  	    query.setAttribute("xmlns","jabber:iq:roster");
  	    query.setParent(packet);
  	    packet.writeXML(session.getWriter());
  	  }

  	  public void sendRosterRemove(String jid) throws IOException {
  	    Packet packet = new Packet("iq");
  	    packet.setType("set");
  	    packet.setID("roster_remove");
  	    Packet query = new Packet("query");
  	    query.setAttribute("xmlns","jabber:iq:roster");
  	    query.setParent(packet);
  	    Packet item = new Packet("item");
  	    item.setAttribute("subscription","remove");
  	    item.setAttribute("jid",jid);
  	    item.setParent(query);
  	    packet.writeXML(session.getWriter());
  	  }

  	  public void sendRosterSet(String jid,
  	                            String name,
  	                            Iterator groups) throws IOException {
  	    Packet packet = new Packet("iq");
  	    packet.setType("set");
  	    packet.setID("roster_set");
  	    Packet query = new Packet("query");
  	    query.setAttribute("xmlns","jabber:iq:roster");
  	    query.setParent(packet);
  	    Packet item = new Packet("item");
  	    item.setAttribute("jid",jid);
  	    item.setAttribute("name",name);
  	    item.setParent(query);
  	    while (groups.hasNext()){
  	      new Packet("group",(String)groups.next()).setParent(item);
  	    }
  	    packet.writeXML(session.getWriter());
  	  }

  	 
}